#include <memory>


#include "desensitize.h"


#define CHANNEL "10003012304"

#define APPID  "luoke.tencent.com"

#define CERT "-----BEGIN CERTIFICATE-----\
MIICpDCCAkugAwIBAgIJMTAwMDAwMTY3MAoGCCqBHM9VAYN1MIHAMQswCQYDVQQG\
EwJDTjESMBAGA1UECAwJR3Vhbmdkb25nMREwDwYDVQQHDAhTaGVuemhlbjE6MDgG\
A1UECgwxU2hlbnpoZW4gVGVuY2VudCBDb21wdXRlciBTeXN0ZW1zIENvbXBhbnkg\
TGltaXRlZDEMMAoGA1UECwwDRmlUMRowGAYDVQQDDBFUZW5jZW50U00gUm9vdCBD\
QTEkMCIGCSqGSIb3DQEJARYVVGVuY2VudFNNQHRlbmNlbnQuY29tMCIYDzIwMjMx\
MTA3MDY1MjI2WhgPMjEyMzEwMTQwNjUyMjZaMIGHMQswCQYDVQQGEwJjbjELMAkG\
A1UECAwCZ2QxCzAJBgNVBAcMAnN6MRUwEwYDVQQKDAxUZW5jZW50IEluYy4xFTAT\
BgNVBAsMDOaZuuaFp+WHuuihjDEUMBIGA1UEAwwLVFNNIGxpY2Vuc2UxGjAYBgNV\
BA0MEWx1b2tlLnRlbmNlbnQuY29tMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE\
9OUsbNCKKH8UCT1RnFPlFNMBJaUTthxY35g34joF/ZiO4VW3sh58IFF2anfZVPA5\
u8NGLaezfhb4UCkJZKyfOaNhMF8wHwYDVR0jBBgwFoAUoHUwm/JhjsLU0gyxBmEG\
zO3vkAwwHQYDVR0OBBYEFO1pOmgECuf5Ia+NqNMeQp2YYgLOMAwGA1UdEwEB/wQC\
MAAwDwYDVR0PAQH/BAUDAwc4ADAKBggqgRzPVQGDdQNHADBEAiB9Pi/U4rfjUsjO\
ZCYRgbcYvG7HEb6PqDDNVn+xHJ6XRgIgdJqProUcl8mdgPkx+UNTrAug8vXktcF1\
rvNP8mNATXE=\
-----END CERTIFICATE-----"

//INIT_LOG(car_scheduler);

namespace car_scheduler {

std::mutex mutex_init_;
std::condition_variable cv_init_;
std::shared_ptr<tencent::IDesensitizeSdk> globalSdk = nullptr;

typedef std::function<void()> OnInitSuccessCallback;
typedef std::function<void(const int32_t error_code, const std::string error_msg)> OnInitErrorCallback;

class InitCallbackImpl :public tencent::InitCallback {
private:
    OnInitSuccessCallback on_task_success_callback_ = nullptr;
    OnInitErrorCallback on_task_error_callback_ = nullptr;
public:
    InitCallbackImpl(const OnInitSuccessCallback &success_callback, const OnInitErrorCallback &fail_callback) {
        on_task_success_callback_ = success_callback;
        on_task_error_callback_ = fail_callback;
    }

    void OnSuccess() {
        if (on_task_success_callback_) {
            on_task_success_callback_();
        }else{
            Log_Info("OnError is null");
        }
    }

    void OnError(int errCode, std::string errMsg) {
        if (on_task_error_callback_) {
            on_task_error_callback_(errCode, errMsg);
        }else{
            Log_Info("OnError is null");
        }
    }
};

Desensitize::~Desensitize() {
  if (globalSdk == nullptr) {
    return;
  }
  globalSdk.reset();
}
Desensitize& Desensitize::getInstance()
{
  static Desensitize des;
  return des;
}
void Desensitize::Init(const std::string& vin_code, const std::string& cache_path) {
  if (globalSdk != nullptr) {
    return;
  }

  globalSdk = tencent::CreateDesensitizeSdk();
  Log_Info("sdk version: {}, channel: {}, deviceId: {}, ca.crtPath: {}",globalSdk->GetSdkVersion(), CHANNEL, vin_code, cache_path);

  tencent::SdkConfig config;
  config.appId = APPID;
  config.cert = CERT;
  config.channel = CHANNEL;
  config.deviceId = vin_code;
  config.dataRootPath = cache_path;
  config.environment = tencent::Environment::kProduct;

  //config.loggerConfig = tencent::LoggerConfig(/*openLog*/true, /*savedToFile*/false, tencent::LogLevel::ERROR);
  config.loggerConfig = std::move(tencent::LoggerConfig(/*openLog*/true, /*savedToFile*/false, tencent::LogLevel::ERROR));
    //启用哪些子模块。例如如果不需要回传，可以关闭回传模块的初始化。
  config.enableModules = static_cast<int>(tencent::SdkModule::kMODULE_GEO_CHECK)
                          | static_cast<int>(tencent::SdkModule::kMODULE_PRIVACY_DESENSITIZE)
                          | static_cast<int>(tencent::SdkModule::kMODULE_UPLOADER);

  config.deleteFileOnUploaded = false;

  config.enableUploadResume = true;

  config.resumeUploadFolder = "";//初始化时会遍历此文件夹进行一次上传

  int ret = RET_ERR;
  int count =5;
  while (ret != RET_OK){
      count--;
      std::unique_lock<std::mutex> lock(mutex_init_);
      bool initOver = false;
      globalSdk->Init(config, std::make_shared<InitCallbackImpl>([&ret, &initOver]() -> void {
          ret = RET_OK;
          initOver = true;
          cv_init_.notify_all();
      }, [&ret, &initOver](const int32_t errCode, const std::string error_msg) -> void {
          (void)error_msg;
          ret = errCode;
          initOver = true;
          cv_init_.notify_all();
      }));
      cv_init_.wait(lock, [&initOver] { return initOver; });

      if (ret != RET_OK){
        Log_Info("Init fail ret={}", ret);
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
      }else{
        break;
      }
      if(count <0)
      {
        Log_Info("Tencent SDK Init failed, retry count:{}",count);
        break;
      }
  }
  if(count >=0)
  {
    Log_Info("Tencent SDK Init success");
    hasInitSdk=true;
  }
}

int Desensitize::DesensitizeFile(const std::string& file_path,const std::string& file_path_new){
  std::string data_in; //not EncryptSM4CBC
  std::string data_out; //EncryptSM4CBC
  ReadDesensitizeFile(file_path,data_in);
  DesensitizeString(data_in,data_out);
  WriteDesensitizeFile(file_path_new,data_out);
  return 0;
}

int Desensitize::DesensitizeString(const std::string& data, std::string& data_new) {
  char* data_new_p = nullptr;
  size_t out_len = 0;
  Log_Info("TENCENT SDK JUDGE HERE!!!");
  if(globalSdk == nullptr) {
    Log_Info("TENCENT SDK IS NULL!!!");
    return -1;
  } 
  globalSdk->EncryptSM4CBC(data.c_str(), data.size(), data_new_p, out_len);
  if (data_new_p == nullptr) {
    return -1;
  }
  data_new = std::string(data_new_p, out_len);
  delete[] data_new_p;
  return 0;
}

int Desensitize::ReadDesensitizeFile(const std::string& file_path, std::string& data) {
   std::ifstream file_stream(file_path.c_str(), std::ios::binary);
   if(!file_stream){
    Log_Info("open file error!");
    return -1;
   }
   std::stringstream str_buffer;
   str_buffer << file_stream.rdbuf();
   data = str_buffer.str();
   file_stream.close();
   return 0;
}


int Desensitize::WriteDesensitizeFile(const std::string& file_path, std::string& data) {
  std::ofstream file_stream(file_path.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
  if(!file_stream){
  Log_Info("open file error!");
  return -1;
  }
  file_stream << data;
  file_stream.close();
  Log_Info("write file success!");
  return 0;
}
bool Desensitize::getHasInitSdk()
{
  return hasInitSdk;
}
}
